package game.server.world;

import game.common.world.PointImpl;
import game.common.world.Vector;
import game.common.world.VectorImpl;
import game.server.world.rtree.RTreeWorld;

import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import java.util.LinkedList;
import java.util.List;

public class GameWorldBenchmark {
	
	private String EOL = System.getProperty("line.separator");
	
	private GameWorld world;
	
	private PrintStream out = System.out;
	
	public GameWorldBenchmark(GameWorld world){
		this.world = world;
		
		this.preload();
	}
	
	/**
	 * this calls every test with count=1 for class loading
	 */
	public void preload(){
		PrintStream oldOut = out;
		
		out = new PrintStream(new OutputStream() {
			@Override
			public void write(int b) throws IOException {
				
			}
		});
		
		// hidden run to load everything
		start(1000);
		
		out = oldOut;
	}
	
	private List<GameObject> newList(){
		return new LinkedList<GameObject>();
	}
	
	public void start(int count){
		StringBuffer msg = new StringBuffer()
		.append("(").append(count).append(")").append(EOL).append(EOL);
		
		List<GameObject> objects = createObjectList(count);
		
		long duration = doAdd(objects);
		addDurationToMessage(msg, "Add objects", duration, count);

		// set dirty flag to objects
		moveObjectPosInList(objects, new VectorImpl(new PointImpl(100, 50), 3, 0));
		
		duration = doUpdate();
		addDurationToMessage(msg, "Update objects", duration, count);

		duration = doVisit(100, 50, 10);
		addDurationToMessage(msg, "Visit objects", duration, count);

		duration = doRemove(objects);
		addDurationToMessage(msg, "Remove Objects", duration, count);
	
		out.println(msg);
		
		objects.clear();
		/*
		this.world.visitObjectsAtRange(0, 0, 100, new GameObjectVisitor() {
			@Override
			public void visit(GameObject obj) {
				world.removeObject(obj);				
			}
		});
		*/
	}
	
	private void addDurationToMessage(StringBuffer sb, String testName, long duration, int count){
		sb
		.append(testName).append(EOL)
		.append("\ttotal: ").append(formatDuration(duration)).append(EOL).append(EOL);
	}
	private List<GameObject> createObjectList(int count){
		List<GameObject> list = newList();
		
		for (int i = 0; i < count;i++){
			list.add(new GameObject(Integer.toString(i)));
		}
		
		return list;
	}
	private void moveObjectPosInList(List<GameObject> list, Vector toVector){
		for (GameObject o : list){
			o.moveTo(toVector);
		}
	}
	
	private long doAdd(List<GameObject> list){
		long start = System.currentTimeMillis();
		
		for (GameObject o : list){
			this.world.addObject(o);
		}
		
		return System.currentTimeMillis() - start;
	}
	private long doRemove(List<GameObject> list){
		long start = System.currentTimeMillis();
		
		for (GameObject o : list){
			this.world.removeObject(o);
		}
		
		return System.currentTimeMillis() - start;
	}
	private long doUpdate(){
		long start = System.currentTimeMillis();
		
		this.world.update();
		
		return System.currentTimeMillis() - start;
	}
	private long doVisit(int x, int y, int range){
		long start = System.currentTimeMillis();
		
		this.world.visitObjectsAtRange(x, y, range, new GameObjectVisitor() {
			@Override
			public void visit(GameObject obj) {
			}
		});
		
		return System.currentTimeMillis() - start;
	}
	
	private String formatDuration(double dur){
		if (dur > 1000){
			return (int)(dur / 1000) + "." + (int)(dur % 1000) + "sec ";
		} else {
			return dur + "ms";
		}
	}
	
	public static void main(String[] args) {
		try {
			GameWorldBenchmark bench = new GameWorldBenchmark(new RTreeWorld());

			bench.start(2000);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}
